package com.yang.mybatis.config.parser;

import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisResultMap;
import com.yang.mybatis.config.MybatisResultMapProperty;
import com.yang.mybatis.config.MybatisSqlStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class XmlMybatisMapperParser implements IMybatisMapperParser {
    private final static Set<String> tagSet = new HashSet<>();

    private final static Set<String> resultMapTagSet = new HashSet<>();

    static {
        tagSet.add("select");
        tagSet.add("insert");
        tagSet.add("update");
        tagSet.add("delete");
        tagSet.add("SELECT");
        tagSet.add("INSERT");
        tagSet.add("UPDATE");
        tagSet.add("DELETE");

        resultMapTagSet.add("resultMap");
        resultMapTagSet.add("ResultMap");
    }
    @Override
    public MybatisMapperXmlConfiguration parseMapper(String path) {
        MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = new MybatisMapperXmlConfiguration();
        try {
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Element root = document.getRootElement();

            parseMapperName(mybatisMapperXmlConfiguration, root);
            for (String tag : tagSet) {
                List<Element> elements = root.elements(tag);
                parseStatement(mybatisMapperXmlConfiguration, elements);
            }
            for (String tag: resultMapTagSet) {
                List<Element> elements = root.elements(tag);
                parseResultMap(mybatisMapperXmlConfiguration, elements);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return mybatisMapperXmlConfiguration;
    }

    private void parseMapperName(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, Element root) {
        String mapperName = root.attributeValue("namespace");
        mybatisMapperXmlConfiguration.setMapperName(mapperName);

    }

    private void parseResultMap(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) {
        if (elements == null || elements.isEmpty()) {
            return;
        }
        for (Element element : elements) {
            String id = element.attributeValue("id");
            String type = element.attributeValue("type");

            MybatisResultMap mybatisResultMap = new MybatisResultMap();
            mybatisResultMap.setId(id);
            mybatisResultMap.setType(type);

            Element idElement = element.element("id");
            if (idElement != null) {
                MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(idElement);
                mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty);
                mybatisResultMap.setIdProperty(mybatisResultMapProperty);
            }

            List<Element> resultList = element.elements("result");
            for (Element resultElement : resultList) {
                MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(resultElement);
                mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty);
            }

            mybatisMapperXmlConfiguration.addMybatisResultMap(mybatisResultMap);
        }
    }

    private MybatisResultMapProperty buildMybatisResultMapProperty(Element element) {
        MybatisResultMapProperty mybatisResultMapProperty = new MybatisResultMapProperty();
        String property = element.attributeValue("property");
        String column = element.attributeValue("column");
        String javaType = element.attributeValue("javaType");
        String jdbcType = element.attributeValue("jdbcType");
        mybatisResultMapProperty.setProperty(property);
        mybatisResultMapProperty.setColumn(column);
        mybatisResultMapProperty.setJavaType(javaType);
        mybatisResultMapProperty.setJdbcType(jdbcType);
        return mybatisResultMapProperty;
    }

    private void parseStatement(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) {
        if (elements == null || elements.isEmpty()) {
            return;
        }
        String namespace = mybatisMapperXmlConfiguration.getMapperName();
        for (Element element : elements) {
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String resultMap = element.attributeValue("resultMap");
            String parameterType = element.attributeValue("parameterType");
            String sql = element.getText().trim();

            MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement();
            mybatisSqlStatement.setNamespace(namespace);
            mybatisSqlStatement.setId(id);
            mybatisSqlStatement.setSql(sql);
            mybatisSqlStatement.setResultType(resultType);
            mybatisSqlStatement.setResultMap(resultMap);
            mybatisSqlStatement.setParameterType(parameterType);
            mybatisSqlStatement.setOperateType(element.getName().toLowerCase());

            mybatisMapperXmlConfiguration.addMybatisSqlStatement(mybatisSqlStatement);
        }
    }
}
